package com.lixixi.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lixixi.tingshu.album.mapper.AlbumInfoMapper;
import com.lixixi.tingshu.album.service.AlbumAttributeValueService;
import com.lixixi.tingshu.album.service.AlbumInfoService;
import com.lixixi.tingshu.album.service.AlbumStatService;
import com.lixixi.tingshu.album.service.AlbumTrackInfoService;
import com.lixixi.tingshu.common.anno.SetCache;
import com.lixixi.tingshu.common.constant.KafkaConstant;
import com.lixixi.tingshu.common.constant.RedisConstant;
import com.lixixi.tingshu.common.constant.SystemConstant;
import com.lixixi.tingshu.common.result.ResultCodeEnum;
import com.lixixi.tingshu.common.util.AuthContextHolder;
import com.lixixi.tingshu.exception.RemoveAlbumException;
import com.lixixi.tingshu.model.album.AlbumAttributeValue;
import com.lixixi.tingshu.model.album.AlbumInfo;
import com.lixixi.tingshu.model.album.AlbumStat;
import com.lixixi.tingshu.model.album.TrackInfo;
import com.lixixi.tingshu.query.album.AlbumInfoQuery;
import com.lixixi.tingshu.vo.album.AlbumAttributeValueVo;
import com.lixixi.tingshu.vo.album.AlbumInfoVo;
import com.lixixi.tingshu.vo.album.AlbumListVo;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Service
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

	@Autowired
	AlbumStatService albumStatService;

	@Autowired
	KafkaTemplate<String, String> kafkaTemplate;

	@Autowired
	private AlbumInfoMapper albumInfoMapper;

	@Autowired
	RedisTemplate<String, String> redisTemplate;

	@Autowired
	RedissonClient redissonClient;

	AlbumTrackInfoService albumTrackInfoService;

	//使用setter注入,避免循环依赖
	@Autowired
	public void setAlbumTrackInfoService(@Lazy AlbumTrackInfoService albumTrackInfoService) {
		this.albumTrackInfoService = albumTrackInfoService;
	}

	@Autowired
	AlbumAttributeValueService albumAttributeValueService;

	//专辑状态的统计类型
	final static List<String> albumStatList = List.of(SystemConstant.ALBUM_STAT_PLAY
			, SystemConstant.ALBUM_STAT_SUBSCRIBE
			, SystemConstant.ALBUM_STAT_BROWSE
			, SystemConstant.ALBUM_STAT_COMMENT);


	@Override
	@Transactional
	public void saveAlbumInfo(AlbumInfo albumInfo, Boolean isUpdate) {
		//填充未设置默认值的字段
		//设置用户id
		Long userId = AuthContextHolder.getUserId();
		albumInfo.setId(userId);
		//专辑价格类型 0201-单集
		albumInfo.setPriceType(SystemConstant.ALBUM_PRICE_TYPE_ONE);
		//设置审核状态, 0301-通过
		albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
		//保存专辑信息
		save(albumInfo);
		//@TableId 打开id回填, 获取保存后的专辑id
		Long albumId = albumInfo.getId();
		//开放,传递消息使专辑上架
		if (!isUpdate)
		{
			//初始化专辑的'统计类型：0401-播放量 0402-订阅量 0403-购买量 0403-评论数'
			albumStatList.forEach(stat -> {
				AlbumStat albumStat = AlbumStat.builder().albumId(albumId).statType(stat).build();
				albumStatService.save(albumStat);
			});
		}
		//获取专辑属性列表
		List<AlbumAttributeValue> albumAttributeValueVoList = albumInfo.getAlbumAttributeValueVoList();
		//设置专辑id
		albumAttributeValueVoList.forEach(albumAttributeValue ->
				albumAttributeValue.setAlbumId(albumId));
		//批量插入专辑属性
		albumAttributeValueService.saveBatch(albumAttributeValueVoList);
		//判断专辑是否开放
		if (SystemConstant.ALBUM_IS_OPEN.equals(albumInfo.getIsOpen()))
		{
			kafkaTemplate.send(KafkaConstant.QUEUE_ALBUM_UPPER, albumId.toString());
		} else
		{
			//不开放,将专辑下架
			kafkaTemplate.send(KafkaConstant.QUEUE_ALBUM_LOWER, albumId.toString());
		}
	}

	@Override
	public IPage<AlbumListVo> findUserAlbumPage(Long page, Long limit, AlbumInfoQuery albumInfoQuery) {
		//获取用户id
		Long userId = AuthContextHolder.getUserId();
		albumInfoQuery.setUserId(userId);
		//查询当前用户下的专辑
		return albumInfoMapper.selectUserAlbumPage(new Page<>(page, limit), albumInfoQuery);
	}

	@Override
	@Transactional
	public void removeAlbumInfoById(Long albumId) {
		//删除专辑缓存
		redisTemplate.delete(RedisConstant.ALBUM_INFO_PREFIX + albumId);
		//如果专辑下还有音频,操作无效
		if (albumTrackInfoService.count(albumTrackInfoService
				.lambdaQuery()
				.eq(TrackInfo::getAlbumId, albumId)) > 0)
		{
			throw new RemoveAlbumException(ResultCodeEnum.ALBUM_REMOVE_ERROR);
		}
		//删除专辑
		removeById(albumId);
		//删除专辑与属性关联表
		albumAttributeValueService.remove(new LambdaQueryWrapper<AlbumAttributeValue>()
				.eq(AlbumAttributeValue::getAlbumId, albumId));
		//删除专辑 '统计类型'
		albumStatService.remove(new LambdaQueryWrapper<AlbumStat>().eq(AlbumStat::getAlbumId, albumId));
		//发送消息,使专辑下架
		kafkaTemplate.send(KafkaConstant.QUEUE_ALBUM_LOWER, albumId.toString());
		try
		{
			//休眠100ms
			Thread.sleep(100);
		} catch (InterruptedException e)
		{
			throw new RuntimeException(e);
		}
		//延迟100ms再次删除缓存
		redisTemplate.delete(RedisConstant.ALBUM_INFO_PREFIX + albumId);
	}
//	不使用aop代理实现缓存
//	@Override
//	public AlbumInfoVo getAlbumInfoVoById(Long albumId) {
//		//查询布隆过滤器是否存在该专辑
//		RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
//		//判断专辑是否存在
//		boolean contains = bloomFilter.contains(albumId);
//			if (Boolean.FALSE.equals(contains))
//			{
//				//不存在则直接返回空值
// 				return null;
//			}
//		//先查询缓存
//		//专辑缓存key
//		String key = RedisConstant.ALBUM_INFO_PREFIX + albumId;
//		//获取redis中缓存
//		try
//		{
//			String cache = redisTemplate.opsForValue().get(key);
//			if (StringUtils.isNotBlank(cache))
//			{
//				//缓存存在直接返回
//				return JSONObject.parseObject(cache, AlbumInfoVo.class);
//			}
//			//设置uuid作为分布式锁唯一标识,避免因业务过长而导致后续锁误删
//			String uuid = UUID.randomUUID().toString().replaceAll("-", "");
//			//缓存获取不到则直接查询数据库,为防止大量请求同时打入数据库对其使用分布式锁进行加锁
//			Boolean absent = redisTemplate.opsForValue().setIfAbsent(RedisConstant.ALBUM_LOCK_SUFFIX + albumId, uuid, 3, TimeUnit.SECONDS);
//			if (Boolean.TRUE.equals(absent))
//			{
//				try
//				{
//					//根据专辑id查询数据
//					AlbumInfo albumInfo = getById(albumId);
//					//model转换为vo
//					AlbumInfoVo albumInfoVo = albumInfoToVo(albumInfo);
//					//将其存入缓存
//					redisTemplate.opsForValue().set(key, JSONObject.toJSONString(albumInfoVo), RedisConstant.ALBUM_TIMEOUT, TimeUnit.SECONDS);
//					return albumInfoVo;
//				} catch (Exception e)
//				{
//					//数据库宕机,发送消息,且打印日志
//					throw new RuntimeException(e);
//				} finally
//				{
//					//获取锁值进行比较
//					String lockUUID = redisTemplate.opsForValue().get(RedisConstant.ALBUM_LOCK_SUFFIX + albumId);
//					//需将判断以及删除动作作为一个动作处理,使其具有原子性
//					//避免在判断与删除之间原有锁已过期且有新的锁设立,从而导致锁误删

	/// /					if(lockUUID.equals(uuid)){
	/// /						//uuid正确则释放锁
	/// /						redisTemplate.delete(RedisConstant.ALBUM_LOCK_SUFFIX + albumId);
	/// /					}
//					//使用lua脚本完成此原子操作
//					redisLuaDeleteLock(RedisConstant.ALBUM_LOCK_SUFFIX + albumId, uuid);
//				}
//			} else
//			{
//				//获取不到锁则休眠300ms随后自旋查询缓存
//				try
//				{
//					Thread.sleep(300);
//					getAlbumInfoById(albumId);
//				} catch (InterruptedException e)
//				{
//					throw new RuntimeException(e);
//				}
//			}
//		} catch (RuntimeException e)
//		{
//			throw new RuntimeException(e);
//		}
//		//有问题直接查询数据库
//		//根据专辑id查询数据
//		AlbumInfo albumInfo = getById(albumId);
//		return albumInfoToVo(albumInfo);
//	}
	//使用aop代理
	@Override
	@SetCache(prefix = RedisConstant.ALBUM_INFO_VO_PREFIX)
	public AlbumInfoVo getAlbumInfoVoById(Long albumId) {
		//查询布隆过滤器是否存在该专辑
		RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
		//判断专辑是否存在
		boolean contains = bloomFilter.contains(albumId);
		if (Boolean.FALSE.equals(contains))
		{
			//不存在则直接返回空值
			return null;
		}
		//数据库查询专辑
		AlbumInfo albumInfo = getById(albumId);
		//查询专辑属性值,用于属性值回显
		List<AlbumAttributeValue> list = albumAttributeValueService.list(new LambdaQueryWrapper<AlbumAttributeValue>()
				.eq(AlbumAttributeValue::getAlbumId, albumInfo.getId())
				.select(AlbumAttributeValue::getAttributeId, AlbumAttributeValue::getValueId));
		return albumInfoToVo(albumInfo, list);
	}

	void redisLuaDeleteLock(String key, String uuid) {
		//定义lua脚本内容
		String luaScript = """
				if redis.call("get",KEYS[1]) == ARGV[1]
				then
				    return redis.call("del",KEYS[1])
				else
				    return 0
				end""";
		//定义lua脚本
		DefaultRedisScript<Long> defaultRedisScript = new DefaultRedisScript<>();
		//设置脚本内容
		defaultRedisScript.setScriptText(luaScript);
		//设置返回属性
		defaultRedisScript.setResultType(Long.class);
		redisTemplate.execute(defaultRedisScript, Collections.singletonList(key), uuid);
	}

	@NotNull
	private AlbumInfoVo albumInfoToVo(AlbumInfo albumInfo, List<AlbumAttributeValue> list) {
		AlbumInfoVo albumInfoVo = BeanUtil.toBean(albumInfo, AlbumInfoVo.class);
		//model转换为vo
		List<AlbumAttributeValueVo> albumAttributeValueVos = list.parallelStream()
				.map(albumAttributeValue ->
						BeanUtil.toBean(albumAttributeValue, AlbumAttributeValueVo.class))
				.collect(Collectors.toList());
		//填充专辑属性列表
		albumInfoVo.setAlbumAttributeValueVoList(albumAttributeValueVos);
		return albumInfoVo;
	}

	@Override
	@SetCache(prefix = RedisConstant.ALBUM_INFO_PREFIX)
	public AlbumInfo getAlbumInfoById(Long albumId) {
		//查询布隆过滤器是否存在该专辑
		RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
		//判断专辑是否存在
		boolean contains = bloomFilter.contains(albumId);
		if (Boolean.FALSE.equals(contains))
		{
			//不存在则直接返回空值
			return null;
		}
		//根据专辑id查询数据
		AlbumInfo albumInfo = getById(albumId);
		//查询专辑属性值,用于属性值回显
		List<AlbumAttributeValue> list = albumAttributeValueService.list(new LambdaQueryWrapper<AlbumAttributeValue>()
				.eq(AlbumAttributeValue::getAlbumId, albumId)
				.select(AlbumAttributeValue::getAttributeId, AlbumAttributeValue::getValueId));
		//填充专辑属性列表
		albumInfo.setAlbumAttributeValueVoList(list);
		return albumInfo;
	}

	@Override
	@Transactional
	public void updateAlbumInfoById(Long oldAlbumId, AlbumInfoVo newAlbumInfoVo) {
		//更新操作需要处理缓存不一致问题
		//采用延迟双删方案
		//进入更新先删除缓存
		redisTemplate.delete(RedisConstant.ALBUM_INFO_PREFIX + oldAlbumId);
		//删除旧专辑
		removeById(oldAlbumId);
		//将专辑下架
		kafkaTemplate.send(KafkaConstant.QUEUE_ALBUM_LOWER, oldAlbumId.toString());
		//删除专辑与属性关联表
		albumAttributeValueService.remove(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, oldAlbumId));
		//vo转换为model
		AlbumInfo albumInfo = BeanUtil.toBean(newAlbumInfoVo, AlbumInfo.class);
		//旧id置空,使新id回填
		albumInfo.setId(null);
		//保存新数据
		saveAlbumInfo(albumInfo, Boolean.TRUE);
		try
		{
			//休眠100ms
			Thread.sleep(100);
		} catch (InterruptedException e)
		{
			throw new RuntimeException(e);
		}
		//更新完成再次删除缓存
		redisTemplate.delete(RedisConstant.ALBUM_INFO_PREFIX + oldAlbumId);
		//更新专辑下关联音频的专辑id
		albumTrackInfoService.update(albumTrackInfoService.lambdaUpdate()
				.eq(TrackInfo::getAlbumId, oldAlbumId)
				.set(TrackInfo::getAlbumId, albumInfo.getId()));
		//更新专辑的'统计类型'
		albumStatService.update(new LambdaUpdateWrapper<AlbumStat>()
				.set(AlbumStat::getAlbumId, albumInfo.getId())
				.eq(AlbumStat::getAlbumId, oldAlbumId));
	}

	@Override
	public List<AlbumInfoVo> findUserAllAlbumList() {
		Long userId = AuthContextHolder.getUserId();
		//查询当前用户下的专辑列表
		List<AlbumInfo> list = this.list(new LambdaQueryWrapper<AlbumInfo>().select(AlbumInfo::getAlbumTitle, AlbumInfo::getId).eq(AlbumInfo::getUserId, userId));
		//model转vo
		//只需向前端传递专辑id与专辑标题
		//处理类中非空字段
		return list.stream().map(albumInfo -> {
			AlbumInfoVo albumInfoVo = new AlbumInfoVo();
			albumInfoVo.setId(albumInfo.getId());
			albumInfoVo.setAlbumTitle(albumInfo.getAlbumTitle());
			//处理类中非空字段
			albumInfoVo.setDiscount(null);
			albumInfoVo.setVipDiscount(null);
			return albumInfoVo;
		}).toList();
	}

	@Override
	public List<AlbumInfo> getUserAlbumInfoByBatch(Long userId) {
		return list(new LambdaQueryWrapper<AlbumInfo>().eq(AlbumInfo::getUserId, userId));
	}
}
